﻿using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using JasLib.Diagnostics;
using JasLib.Interop;
using JasLib.Interop.Win32;
using Microsoft.Win32.SafeHandles;
using System.Collections.Concurrent;

namespace JasLib.Interop.Win32
{
    public static partial class KERNEL32
    {
        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CallbackMayRunLong", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CallbackMayRunLong([In] PTP_CALLBACK_INSTANCE pci);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CancelThreadpoolIo", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void CancelThreadpoolIo([In] PTP_IO pio);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CloseThreadpool", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void CloseThreadpool([In] UINT_PTR ptpp);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CloseThreadpoolCleanupGroup", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void CloseThreadpoolCleanupGroup([In] UINT_PTR ptpcg);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CloseThreadpoolCleanupGroupMembers", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void CloseThreadpoolCleanupGroupMembers(
            [In] PTP_CLEANUP_GROUP ptpcg,
            [In, MarshalAs(UnmanagedType.Bool)] bool fCancelPendingCallbacks,
            [In] UINT_PTR pvCleanupContext);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CloseThreadpoolIo", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void CloseThreadpoolIo([In] UINT_PTR pio);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CloseThreadpoolTimer", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void CloseThreadpoolTimer([In] UINT_PTR pti);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CloseThreadpoolWait", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void CloseThreadpoolWait([In] UINT_PTR pwa);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CloseThreadpoolWork", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void CloseThreadpoolWork([In] UINT_PTR pwk);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CreateThreadpool", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern PTP_POOL CreateThreadpool([In] UINT_PTR reserved);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CreateThreadpoolCleanupGroup", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern PTP_CLEANUP_GROUP CreateThreadpoolCleanupGroup();

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CreateThreadpoolIo", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern PTP_IO CreateThreadpoolIo(
            [In] SafeFileHandle fl,
            [In] PTP_WIN32_IO_CALLBACK pfnio,
            [In] UINT_PTR pv,
            [In] ref TP_CALLBACK_ENVIRON_V1 pcbe);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CreateThreadpoolTimer", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern PTP_TIMER CreateThreadpoolTimer(
            [In, MarshalAs(UnmanagedType.FunctionPtr)] PTP_TIMER_CALLBACK pfnti,
            [In] UINT_PTR pv,
            [In] ref TP_CALLBACK_ENVIRON_V1 pcbe);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CreateThreadpoolTimer", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern PTP_TIMER CreateThreadpoolTimer(
            [In, MarshalAs(UnmanagedType.FunctionPtr)] PTP_TIMER_CALLBACK pfnti,
            [In] IntPtr pv,
            [In] IntPtr pcbe);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CreateThreadpoolTimer", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern PTP_TIMER CreateThreadpoolTimer(
            [In] IntPtr pfnti,
            [In] IntPtr pv,
            [In] IntPtr pcbe);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CreateThreadpoolWait", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern PTP_WAIT CreateThreadpoolWait(
            [In, MarshalAs(UnmanagedType.FunctionPtr)] PTP_WAIT_CALLBACK pfnwa,
            [In] UINT_PTR pv,
            [In] ref TP_CALLBACK_ENVIRON_V1 pcbe);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CreateThreadpoolWait", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern PTP_WAIT CreateThreadpoolWait(
            [In, MarshalAs(UnmanagedType.FunctionPtr)] PTP_WAIT_CALLBACK pfnwa,
            [In] IntPtr pv,
            [In] IntPtr pcbe);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CreateThreadpoolWait", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern PTP_WAIT CreateThreadpoolWait(
            [In] IntPtr pfnwa,
            [In] IntPtr pv,
            [In] IntPtr pcbe);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CreateThreadpoolWork", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern PTP_WORK CreateThreadpoolWork(
            [In, MarshalAs(UnmanagedType.FunctionPtr)] PTP_WORK_CALLBACK pfnwk,
            [In] UINT_PTR pv,
            [In] ref TP_CALLBACK_ENVIRON_V1 pcbe);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CreateThreadpoolWork", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern PTP_WORK CreateThreadpoolWork(
            [In] IntPtr pfnwk,
            [In] IntPtr pv,
            [In] IntPtr pcbe);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "CreateThreadpoolWork", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern PTP_WORK CreateThreadpoolWork(
            [In, MarshalAs(UnmanagedType.FunctionPtr)] PTP_WORK_CALLBACK pfnwk,
            [In] IntPtr pv,
            [In] IntPtr pcbe);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "DisassociateCurrentThreadFromCallback", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void DisassociateCurrentThreadFromCallback(PTP_CALLBACK_INSTANCE pci);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "FreeLibraryWhenCallbackReturns", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void FreeLibraryWhenCallbackReturns(
            [In] PTP_CALLBACK_INSTANCE pci,
            [In] HINSTANCE mod);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "IsThreadpoolTimerSet", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool IsThreadpoolTimerSet([In] PTP_TIMER pti);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "LeaveCriticalSectionWhenCallbackReturns", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void LeaveCriticalSectionWhenCallbackReturns(
            [In] PTP_CALLBACK_INSTANCE pci,
            [In] UINT_PTR /* PCRITICAL_SECTION */ pcs);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "ReleaseMutexWhenCallbackReturns", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void ReleaseMutexWhenCallbackReturns(
            [In] PTP_CALLBACK_INSTANCE pci,
            [In] SafeWaitHandle mut);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "ReleaseSemaphoreWhenCallbackReturns", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void ReleaseSemaphoreWhenCallbackReturns(
            [In] PTP_CALLBACK_INSTANCE pci,
            [In] SafeWaitHandle sem,
            [In] uint crel);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "ReleaseSemaphoreWhenCallbackReturns", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void SetEventWhenCallbackReturns(
            [In] PTP_CALLBACK_INSTANCE pci,
            [In] SafeWaitHandle evt);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "SetThreadpoolThreadMaximum", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void SetThreadpoolThreadMaximum(
            [In] PTP_POOL ptpp,
            [In] uint cthrdMost);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "SetThreadpoolThreadMinimum", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void SetThreadpoolThreadMinimum(
            [In] PTP_POOL ptpp,
            [In] uint cthrdMic);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "SetThreadpoolTimer", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void SetThreadpoolTimer(
            [In] PTP_TIMER pti,
            [In] ref FILETIME pftDueTime,
            [In] uint msPeriod,
            [In] uint msWindowLength);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "SetThreadpoolWait", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void SetThreadpoolWait(
            [In] PTP_WAIT pwa,
            [In] SafeWaitHandle h,
            [In] ref FILETIME pftTimeout);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "StartThreadpoolIo", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void StartThreadpoolIo(PTP_IO pio);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "SubmitThreadpoolWork", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void SubmitThreadpoolWork(PTP_WORK pwk);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "TrySubmitThreadpoolCallback", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool TrySubmitThreadpoolCallback(
            [In, MarshalAs(UnmanagedType.FunctionPtr)] PTP_SIMPLE_CALLBACK pfns,
            [In] UINT_PTR pv,
            [In] ref TP_CALLBACK_ENVIRON_V1 pcbe);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "TrySubmitThreadpoolCallback", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool TrySubmitThreadpoolCallback(
            [In] IntPtr pfns,
            [In] IntPtr pv,
            [In] IntPtr pcbe);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "WaitForThreadpoolIoCallbacks", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void WaitForThreadpoolIoCallbacks(
            [In] PTP_IO pio,
            [In, MarshalAs(UnmanagedType.Bool)] bool fCancelPendingCallbacks);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "WaitForThreadpoolTimerCallbacks", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void WaitForThreadpoolTimerCallbacks(
            [In] PTP_TIMER pti,
            [In, MarshalAs(UnmanagedType.Bool)] bool fCancelPendingCallbacks);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "WaitForThreadpoolWaitCallbacks", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void WaitForThreadpoolWaitCallbacks(
            [In] PTP_WAIT pwa,
            [In, MarshalAs(UnmanagedType.Bool)] bool fCancelPendingCallbacks);

        [DllImport(Constants.ModuleNames.KERNEL32, EntryPoint = "WaitForThreadpoolWorkCallbacks", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern void WaitForThreadpoolWorkCallbacks(
            [In] PTP_WORK pwk,
            [In, MarshalAs(UnmanagedType.Bool)] bool fCancelPendingCallbacks);
    }

    public static partial class Win32InlineFunctions
    {
        public static void DestroyThreadpoolEnvironment(ref TP_CALLBACK_ENVIRON_V1 pcbe)
        {
            /// From "WinNT.h":
            /// "For the current version of the callback environment, no actions
            /// need to be taken to tear down an initialized structure.  This
            /// may change in a future release.
            return;
        }

        public static void DestroyThreadpoolEnvironment(IntPtr pcbe)
        {
            /// From "WinNT.h":
            /// "For the current version of the callback environment, no actions
            /// need to be taken to tear down an initialized structure.  This
            /// may change in a future release.
            return;
        }

        public static void InitializeThreadpoolEnvironment(out TP_CALLBACK_ENVIRON_V1 pcbe)
        {
            pcbe = new TP_CALLBACK_ENVIRON_V1
            {
                Version = 1,
                Pool = 0,
                CleanupGroup = 0,
                CleanupGroupCancelCallback = null,
                RaceDll = HINSTANCE.NULL,
                ActivationContext = 0,
                FinalizationCallback = null,
                Flags = 0,
            };
        }

        public static void InitializeThreadpoolEnvironment(out TP_CALLBACK_ENVIRON_V3 pcbe)
        {
            pcbe = new TP_CALLBACK_ENVIRON_V3
            {
                Version = 3,
                Pool = 0,
                CleanupGroup = 0,
                CleanupGroupCancelCallback = null,
                RaceDll = HINSTANCE.NULL,
                ActivationContext = 0,
                FinalizationCallback = null,
                Flags = 0,
                CallbackPriority = TP_CALLBACK_PRIORITY.TP_CALLBACK_PRIORITY_NORMAL,
                Size = MarshalHelper.SizeOf<TP_CALLBACK_ENVIRON_V3>(),
            };
        }

        public static unsafe void InitializeThreadpoolEnvironment(TP_CALLBACK_ENVIRON* pcbe)
        {
            pcbe->Pool = 0;
            pcbe->CleanupGroup = 0;
            pcbe->CleanupGroupCancelCallback = IntPtr.Zero;
            pcbe->RaceDll = HINSTANCE.NULL;
            pcbe->ActivationContext = 0;
            pcbe->FinalizationCallback = IntPtr.Zero;
            pcbe->Flags = 0;

            if (JasLib.Interop.Win32.Environment.IsWindows7OrGreater)
            {
                pcbe->Version = 3;
                pcbe->CallbackPriority = TP_CALLBACK_PRIORITY.TP_CALLBACK_PRIORITY_NORMAL;
                pcbe->Size = MarshalHelper.SizeOf<TP_CALLBACK_ENVIRON_V3>();
            }
            else
            {
                pcbe->Version = 1;
            }
        }

        public static void SetThreadpoolCallbackCleanupGroup(
            ref TP_CALLBACK_ENVIRON_V1 pcbe,
            PTP_CLEANUP_GROUP ptpcg,
            PTP_CLEANUP_GROUP_CANCEL_CALLBACK pfng)
        {
            pcbe.CleanupGroup = ptpcg;
            pcbe.CleanupGroupCancelCallback = pfng;
            return;
        }

        public static void SetThreadpoolCallbackLibrary(
            ref TP_CALLBACK_ENVIRON_V1 pcbe,
            HINSTANCE mod)
        {
            pcbe.RaceDll = mod;
            return;
        }

        public static void SetThreadpoolCallbackPool(
            ref TP_CALLBACK_ENVIRON_V1 pcbe,
            PTP_POOL ptpp)
        {
            pcbe.Pool = ptpp;
            return;
        }

        public static unsafe void SetThreadpoolCallbackPool(
            TP_CALLBACK_ENVIRON* pcbe,
            PTP_POOL ptpp)
        {
            pcbe->Pool = ptpp;
            return;
        }

        public static void SetThreadpoolCallbackPriority(
            ref TP_CALLBACK_ENVIRON_V3 pcbe,
            TP_CALLBACK_PRIORITY Priority)
        {
            pcbe.CallbackPriority = Priority;
            return;
        }

        public static void SetThreadpoolCallbackRunsLong(
            ref TP_CALLBACK_ENVIRON_V1 pcbe)
        {
            /// CallbackEnviron->u.s.LongFunction = 1
            pcbe.Flags &= 1;
            return;
        }
    }
}

namespace JasLib.Threading
{
    public interface IThreadPoolObject : IDisposable
    {
    }

    public interface IThreadPoolTimer : IThreadPoolObject
    {
        void Set(
            TimeSpan dueTimeFromNow,
            TimeSpan? recurringPeriodAfterFirstDue = null,
            TimeSpan? gatherWindowLength = null);
        bool IsSet { get; }
    }

    public interface IThreadPoolKernelWait : IThreadPoolObject
    {
    }

    public interface IThreadPoolIo : IThreadPoolObject
    {
    }

    /// <summary>
    /// You could think of this as SynchronizationContext 2.0.
    /// </summary>
    public interface IThreadPool : IDisposable
    {
        IThreadPoolKernelWait RegisterWaitForSingleObject(
            SafeWaitHandle waitHandle,
            WaitOrTimerCallback callBack,
            object state,
            TimeSpan timeout,
            bool executeOnlyOnce);
        IThreadPoolTimer CreateTimer();
    }

    [DebuggerDisplay("ActiveItemsInQueue = {ActiveItemsInQueue}")]
    public partial class VistaThreadPool : FinalizedDisposableObject
    {
        /***********************************************************************************************/
        private static VistaThreadPool s_defaultThreadPool = new VistaThreadPool(false);
        public static VistaThreadPool Default
        {
            get { return s_defaultThreadPool; }
        }

        /***********************************************************************************************/
        static VistaThreadPool()
        {
            DebugUtilities.Assert(JasLib.Interop.Win32.Environment.IsWindowsVistaOrGreater);
            return;
        }

        /***********************************************************************************************/
        private class VistaThreadPoolSynchronizationContext : ThreadPoolSynchronizationContext
        {
            private VistaThreadPool _pool = null;
            public VistaThreadPool Pool
            {
                get { return _pool; }
            }

            public VistaThreadPoolSynchronizationContext(VistaThreadPool pool)
            {
                if (pool == null)
                    throw new ArgumentNullException("pool");
                _pool = pool;
                return;
            }

            public override void Post(SendOrPostCallback d, object state)
            {
                _pool.QueueUserWorkItem(d, state);
                return;
            }

            public override bool CanSendSynchronously
            {
                get
                {
                    /// Cheap.
                    return (SynchronizationContext.Current is VistaThreadPoolSynchronizationContext);
                }
            }
        }

        /***********************************************************************************************/
        private PTP_POOL _nativePoolHandle = PTP_POOL.NULL;
        //private TP_CALLBACK_ENVIRON_V1 _nativeCallbackEnvironment;
        private IntPtr _pNativeCallbackEnvironment = KERNEL32.HeapAlloc(MarshalHelper.SizeOf<TP_CALLBACK_ENVIRON_V1>());
        private VistaThreadPoolSynchronizationContext _synchronizationContext = null;

        private ConcurrentQueue<Tuple<SendOrPostCallback, object>> _userWorkItems = new ConcurrentQueue<Tuple<SendOrPostCallback, object>>();
        private RepeatableUserPoolItemContext _queueProcessorWorkItem = null;

        /***********************************************************************************************/
        private VistaThreadPool(bool bAllocateNewPool)
        {
            _synchronizationContext = new VistaThreadPoolSynchronizationContext(this);

            unsafe
            {
                Win32InlineFunctions.InitializeThreadpoolEnvironment((TP_CALLBACK_ENVIRON*)_pNativeCallbackEnvironment);

                if (bAllocateNewPool)
                {
                    _nativePoolHandle = KERNEL32.CreateThreadpool(UINT_PTR.Zero);
                    Win32InlineFunctions.SetThreadpoolCallbackPool((TP_CALLBACK_ENVIRON*)_pNativeCallbackEnvironment, _nativePoolHandle);
                }
            }

            _queueProcessorWorkItem = new RepeatableUserPoolItemContext(this, this.OnUserWorkItem, null);
            return;
        }

        /***********************************************************************************************/
        public VistaThreadPool()
            : this(true)
        {
            return;
        }

        /***********************************************************************************************/
        private AsyncManualResetEvent _queueEmptyEvent = new AsyncManualResetEvent(true);

        private volatile int _iActiveItems = 0;
        public int ActiveItems
        {
            get { return _iActiveItems; }
        }

        private void IncrementActiveItems()
        {
            if (Interlocked.Increment(ref _iActiveItems) == 1)
                _queueEmptyEvent.Reset();
            return;
        }

        private void DecrementActiveItems()
        {
            if (Interlocked.Decrement(ref _iActiveItems) == 0)
                _queueEmptyEvent.Set();
            return;
        }

        public Task WaitForIdleAsync(CancellationToken cancellationToken)
        {
            return TaskEx.WhenAny(
                TaskEx.Delay(-1, cancellationToken),
                _queueEmptyEvent.WaitAsync());
        }

        /***********************************************************************************************/
        private volatile int _iMaxThreadCount = 500;
        public int MaxThreadCount
        {
            get { return _iMaxThreadCount; }
            set
            {
                KERNEL32.SetThreadpoolThreadMaximum(_nativePoolHandle, (uint)value);
                _iMaxThreadCount = value;
            }
        }

        private volatile int _iMinThreadCount = 0;
        public int MinThreadCount
        {
            get { return _iMinThreadCount; }
            set
            {
                KERNEL32.SetThreadpoolThreadMinimum(_nativePoolHandle, (uint)value);
                _iMinThreadCount = value;
            }
        }

        /***********************************************************************************************/
        private class PoolItemContext
        {
            public bool _bCancelled = false;
            public VistaThreadPool _pool = null;
            public object _objState = null;

            public PoolItemContext(VistaThreadPool pool, object objState)
            {
                _pool = pool;
                _objState = objState;
                return;
            }

            protected virtual void DoCallback()
            {
            }

            public void Run()
            {
                try
                {
                    if (!_bCancelled)
                    {
                        SynchronizationContext.SetSynchronizationContext(_pool._synchronizationContext);

                        var thread = Thread.CurrentThread;
                        if (thread.Name == null)
                            thread.Name = "Native Pool Thread";

                        DoCallback();
                    }
                }
                catch
                {
                    throw;
                }
                finally
                {
                    _pool.DecrementActiveItems();
                }
            }
        }

        /***********************************************************************************************/
        /*private class SingleUserPoolItemContext : PoolItemContext
        {
            private static void Callback(PTP_CALLBACK_INSTANCE Instance, UINT_PTR Context)
            {
                var handle = GCHandle.FromIntPtr(Context);
                var poolItemContext = (SingleUserPoolItemContext)handle.Target;
                poolItemContext._gcHandle.Free();

                poolItemContext.Run();
                return;
            }

            private static readonly PTP_SIMPLE_CALLBACK s_callbackDelegate = new PTP_SIMPLE_CALLBACK(Callback);
            private static readonly IntPtr s_pfnCallback = Marshal.GetFunctionPointerForDelegate(s_callbackDelegate);

            public SendOrPostCallback _callback = null;

            public SingleUserPoolItemContext(VistaThreadPool pool, SendOrPostCallback callback, object objState)
                : base(pool, objState)
            {
                _callback = callback;

                if (!KERNEL32.TrySubmitThreadpoolCallback(s_pfnCallback, _iGCHandle, _pool._pNativeCallbackEnvironment))
                    throw new Win32Exception();
            }

            protected override void DoCallback()
            {
                _callback(_objState);
                return;
            }
        }*/

        /***********************************************************************************************/
        private class RepeatableUserPoolItemContext : PoolItemContext
        {
            private PTP_WORK _workHandle = null;
            private readonly PTP_WORK_CALLBACK _nativeCallback = null;
            public readonly SendOrPostCallback _userCallback = null;

            public RepeatableUserPoolItemContext(VistaThreadPool pool, SendOrPostCallback userCallback, object objState)
                : base(pool, objState)
            {
                _userCallback = userCallback;
                _nativeCallback = new PTP_WORK_CALLBACK(this.InstanceCallback);
                _workHandle = KERNEL32.CreateThreadpoolWork(_nativeCallback, IntPtr.Zero, _pool._pNativeCallbackEnvironment);
                return;
            }

            protected void InstanceCallback(PTP_CALLBACK_INSTANCE Instance, UINT_PTR Context, UINT_PTR Work)
            {
                Run();
                return;
            }

            protected override void DoCallback()
            {
                _userCallback(_objState);
                return;
            }

            public void QueueOnce()
            {
                /// TODO: We really need these as an atomic operation.
                KERNEL32.SubmitThreadpoolWork(_workHandle);
                _pool.IncrementActiveItems();
                return;
            }

            public void WaitForAll()
            {
                KERNEL32.WaitForThreadpoolWorkCallbacks(_workHandle, false);
                return;
            }

            #region IDisposable Members

            public void Dispose()
            {
                var poolHandle = Interlocked.Exchange(ref _workHandle, null);
                if (poolHandle != null)
                    poolHandle.Dispose();
                _bCancelled = true;
                return;
            }

            #endregion
        }

        /***********************************************************************************************/
        private class TimerPoolItemContext : PoolItemContext, IThreadPoolTimer
        {
            public PTP_TIMER _timerPoolHandle = null;
            public readonly PTP_TIMER_CALLBACK _nativeCallback = null;
            public readonly Action<object> _userCallback = null;

            public TimerPoolItemContext(VistaThreadPool pool, Action<object> callback, object objState)
                : base(pool, objState)
            {
                _userCallback = callback;
                _nativeCallback = new PTP_TIMER_CALLBACK(this.InstanceCallback);
                _timerPoolHandle = KERNEL32.CreateThreadpoolTimer(_nativeCallback, IntPtr.Zero, _pool._pNativeCallbackEnvironment);
                return;
            }

            private void InstanceCallback(PTP_CALLBACK_INSTANCE Instance, UINT_PTR Context, UINT_PTR Timer)
            {
                Run();
                return;
            }

            protected override void DoCallback()
            {
                _userCallback(_objState);
                return;
            }

            #region IDisposable Members

            public void Dispose()
            {
                var poolHandle = Interlocked.Exchange(ref _timerPoolHandle, null);
                if (poolHandle != null)
                    poolHandle.Dispose();
                _bCancelled = true;
                return;
            }

            #endregion

            #region IThreadPoolTimer Members

            public void Set(
                TimeSpan dueTimeFromNow,
                TimeSpan? recurringPeriodAfterFirstDue = null,
                TimeSpan? gatherWindowLength = null)
            {
                JasLib.Interop.Win32.FILETIME dueFileTime = -dueTimeFromNow;
                if (recurringPeriodAfterFirstDue == null)
                    recurringPeriodAfterFirstDue = TimeSpan.Zero;
                if (gatherWindowLength == null)
                    gatherWindowLength = TimeSpan.Zero;
                KERNEL32.SetThreadpoolTimer(
                    _timerPoolHandle,
                    ref dueFileTime,
                    (uint)recurringPeriodAfterFirstDue.Value.TotalMilliseconds,
                    (uint)gatherWindowLength.Value.TotalMilliseconds);
                return;
            }

            public bool IsSet
            {
                get { return KERNEL32.IsThreadpoolTimerSet(_timerPoolHandle); }
            }

            #endregion
        }

        /***********************************************************************************************/
        private class WaitPoolItemContext : PoolItemContext, IThreadPoolKernelWait
        {
            public PTP_WAIT _waitPoolHandle = null;
            private readonly PTP_WAIT_CALLBACK _nativeCallback = null;
            private readonly WaitOrTimerCallback _userCallback = null;
            public SafeWaitHandle _safeWaitHandle = null;
            public JasLib.Interop.Win32.FILETIME _timeout = new JasLib.Interop.Win32.FILETIME();
            public bool _bExecuteOnlyOnce = true;

            public uint _uiWaitResult = 0;

            public WaitPoolItemContext(VistaThreadPool pool, WaitOrTimerCallback callback, object objState)
                : base(pool, objState)
            {
                _userCallback = callback;
                _nativeCallback = new PTP_WAIT_CALLBACK(this.InstanceCallback);
                _waitPoolHandle = KERNEL32.CreateThreadpoolWait(_nativeCallback, IntPtr.Zero, _pool._pNativeCallbackEnvironment);
                return;
            }

            private void InstanceCallback(
                PTP_CALLBACK_INSTANCE Instance,
                UINT_PTR Context,
                UINT_PTR Wait,
                uint WaitResult)
            {
                _uiWaitResult = WaitResult;
                Run();
                return;
            }

            protected override void DoCallback()
            {
                _userCallback(_objState, _uiWaitResult == Constants.WAIT_TIMEOUT);

                if (_bExecuteOnlyOnce)
                {
                    Dispose();
                }
                else
                {
                    Register();
                }
                return;
            }

            public void Register()
            {
                KERNEL32.SetThreadpoolWait(_waitPoolHandle, _safeWaitHandle, ref _timeout);
                _pool.IncrementActiveItems();
            }

            #region IDisposable Members

            public void Dispose()
            {
                var poolHandle = Interlocked.Exchange(ref _waitPoolHandle, null);
                if (poolHandle != null)
                    poolHandle.Dispose();
                _bCancelled = true;
                return;
            }

            #endregion
        }

        /***********************************************************************************************/
        public void QueueUserWorkItem(SendOrPostCallback d, object objState = null)
        {
            _userWorkItems.Enqueue(Tuple.Create(d, objState));

            /// TODO: Find out if we can optimize/reduce the frequency of calling this.
            _queueProcessorWorkItem.QueueOnce();
            return;
        }

        /***********************************************************************************************/
        public void OnUserWorkItem(object objState)
        {
            Tuple<SendOrPostCallback, object> userWorkItem;
            while (this._userWorkItems.TryDequeue(out userWorkItem))
            {
                userWorkItem.Item1(userWorkItem.Item2);
            }
            return;
        }

        /***********************************************************************************************/
        public IThreadPoolKernelWait RegisterWaitForSingleObject(
            SafeWaitHandle safeWaitHandle,
            WaitOrTimerCallback d,
            object objState,
            TimeSpan timeout,
            bool bExecuteOnlyOnce = true)
        {
            var poolItemContext = new WaitPoolItemContext(this, d, objState)
            {
                _safeWaitHandle = safeWaitHandle,
                _timeout = timeout,
                _bExecuteOnlyOnce = bExecuteOnlyOnce,
            };

            poolItemContext.Register();
            return poolItemContext;
        }

        /***********************************************************************************************/
        public IThreadPoolTimer CreateTimer(
            Action<object> d,
            object objState)
        {
            return new TimerPoolItemContext(this, d, objState);
        }

        /***********************************************************************************************/
        protected override void Dispose(bool bFinalizer)
        {
            if (!bFinalizer)
            {
                if (_nativePoolHandle != null)
                {
                }
                /// TODO: WaitForThreadpoolWorkCallbacks() etc.
            }

            Win32InlineFunctions.DestroyThreadpoolEnvironment(_pNativeCallbackEnvironment);
            return;
        }

        /***********************************************************************************************/
        private class UnsafeSwitchToVistaThreadPoolAwaitable : UnsafeAwaitable
        {
            private VistaThreadPool _pool = null;

            public UnsafeSwitchToVistaThreadPoolAwaitable(VistaThreadPool pool)
            {
                if (pool == null)
                    throw new ArgumentNullException("pool");
                _pool = pool;
                return;
            }

            public override bool IsCompleted
            {
                get
                {
                    /// Keep this a very simple dummy check. It should be correct most of the time.
                    var context = (SynchronizationContext.Current as VistaThreadPoolSynchronizationContext);
                    return (context != null && context.Pool == _pool);
                }
            }

            public override void OnCompleted(Action continuation)
            {
                _pool.QueueUserWorkItem(obj => ((Action)obj)(), continuation);
                return;
            }
        }

        public UnsafeAwaitable SwitchToAsync()
        {
            return new UnsafeSwitchToVistaThreadPoolAwaitable(this);
        }
    }
}
